package com.wenjie.terminal.service;

import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import com.pantum.lld.DeviceInfo;
import com.pantum.lld.DeviceManager;
import com.pantum.lld.PrintSetting;
import com.wenjie.terminal.config.App;
import com.wenjie.terminal.config.Common;
import com.wenjie.terminal.entity.CResponse;
import com.wenjie.terminal.entity.MyUsbDevice;
import com.wenjie.terminal.entity.PrinterConfig;
import com.wenjie.terminal.util.CLog;
import com.wenjie.terminal.util.ConfigurationUtil;
import com.wenjie.terminal.util.FileUtil;
import com.wenjie.terminal.util.MapUtil;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Function;

import fi.iki.elonen.NanoHTTPD;

/**
 * HTTP服务器
 */
public class HttpServer extends NanoHTTPD {

    private static final String JSON_MIME = "application/json";

    private final App app;
    private final Context mContext;
    private final Map<String, Function<IHTTPSession, Response>> routes = new HashMap<>();

    public HttpServer(App app) throws IOException {
        super(Common.SERVER_PORT);
        this.app = app;
        this.mContext = app.getContext();

        defineRoutes();

        // 启动服务
        start(SOCKET_READ_TIMEOUT, true);

        CLog.d("===> HTTP服务已启动");
    }

    /**
     * 定义访问路由
     */
    private void defineRoutes() {
        addRoute("/", this::handleRoot);
        addRoute("/ping", this::handlePing);
        addRoute("/getAllUsbDevice", this::handleGetAllUsbDevice);
        addRoute("/setPrinter", this::handleSetPrinter);
        addRoute("/printerAbility", this::getPrinterAbility);
        addRoute("/configPrinter", this::configPrinter);
        addRoute("/clearPrintQueue", this::clearPrintQueue);
        addRoute("/addPrintFile", this::addPrintFile);
        addRoute("/startPrint", this::startPrint);
        addRoute("/cancelPrint", this::cancelPrint);
    }

    private void addRoute(String path, Function<IHTTPSession, Response> handler) {
        routes.put(path, handler);
    }

    /**
     * 访问HTTP服务首页
     */
    private Response handleRoot(IHTTPSession session) {
        return handleServerResponse(CResponse.successWithStrJson("服务器运行正常"));
    }

    /**
     * 服务器运行状态测试
     */
    private Response handlePing(IHTTPSession session) {
        return handleServerResponse(CResponse.successWithStrJson("pong"));
    }

    /**
     * 获取所有的USB设备
     */
    private Response handleGetAllUsbDevice(IHTTPSession session) {
        List<UsbDevice> usbDeviceList = app.getUsbDeviceList();
        List<MyUsbDevice> targetList = new ArrayList<>();

        if (usbDeviceList != null) {
            for (UsbDevice usbDevice : usbDeviceList) {
                MyUsbDevice myUsbDevice = new MyUsbDevice();
                myUsbDevice.setDeviceId(usbDevice.getDeviceId());
                myUsbDevice.setDeviceName(usbDevice.getProductName());

                targetList.add(myUsbDevice);
            }
        } else {
            return handleServerResponse(CResponse.errorWithStrJson("没有可用的USB设备"));
        }

        return handleServerResponse(CResponse.successJson(targetList));
    }

    /**
     * 设置打印机设备
     */
    private Response handleSetPrinter(IHTTPSession session) {
        Method method = session.getMethod();
        if (Method.POST != method) {
            return handleServerResponse(CResponse.errorWithStrJson("请求方法不支持"));
        }

        // 解析请求体
        try {
            session.parseBody(new HashMap<>());
        } catch (Exception ex) {
            ex.printStackTrace();

            return this.handleServerResponse(CResponse.errorWithStrJson("请求体解析异常"));
        }

        Map<String, List<String>> parameters = session.getParameters();
        if (parameters == null || parameters.isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("无法解析到任何请求参数"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        List<String> usbDeviceIdList = parameters.get("usbDeviceId");
        if (usbDeviceIdList == null || usbDeviceIdList.isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请选择打印机设备"));
        }

        String usbDeviceIdStr = usbDeviceIdList.get(0);
        int usbDeviceId;
        try {
            usbDeviceId = Integer.parseInt(usbDeviceIdStr);
        } catch (Exception ex) {
            return this.handleServerResponse(CResponse.errorWithStrJson("USB设备信息不正确"));
        }

        List<UsbDevice> usbDeviceList = app.getUsbDeviceList();
        final int finalUsbDeviceId = usbDeviceId;
        Optional<UsbDevice> usbDeviceOptional = usbDeviceList.stream().filter(item -> Objects.equals(item.getDeviceId(), finalUsbDeviceId)).findAny();
        if (usbDeviceOptional.isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("所选择的USB设备不存在"));
        }

        UsbDevice usbDevice = usbDeviceOptional.get();

        UsbManager usbManager = (UsbManager) mContext.getSystemService(Context.USB_SERVICE);
        if (!usbManager.hasPermission(usbDevice)) {
            // 没有权限，申请权限
            PendingIntent permissionIntent = PendingIntent.getBroadcast(
                    mContext,
                    110,
                    new Intent("com.wenjie.ACTION_USB_PERMISSION"),
                    PendingIntent.FLAG_IMMUTABLE
            );
            usbManager.requestPermission(usbDevice, permissionIntent);

            return this.handleServerResponse(CResponse.errorWithStrJson("请点击允许使用此USB设备，并再次提交请求"));
        }

        final int fileDescriptor = usbManager.openDevice(usbDevice).getFileDescriptor();
        final int productId = usbDevice.getProductId();
        final int vendorId = usbDevice.getVendorId();
        final String productName = usbDevice.getProductName();

        DeviceInfo deviceInfo = new DeviceInfo(productName, productName, 2, String.valueOf(fileDescriptor), false);

        boolean result = printDevice.setDevice(deviceInfo);
        if (result) {
            // 保存打印机配置
            ConfigurationUtil configurationUtil = app.getConfigurationUtil();
            configurationUtil.putInt(Common.PRINTER_PID, productId);
            configurationUtil.putInt(Common.PRINTER_VID, vendorId);

            app.setConfigPrinter(true);

            return handleServerResponse(CResponse.successWithStrJson("打印机设置成功"));
        } else {
            return handleServerResponse(CResponse.errorWithStrJson("打印机设置失败，请稍后重试"));
        }
    }

    /**
     * 获取打印机能力
     */
    private Response getPrinterAbility(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        String deviceCapability = printDevice.getDeviceCapability();
        return handleServerResponse(CResponse.successJson(deviceCapability));
    }

    /**
     * 打印机配置
     */
    private Response configPrinter(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        Method method = session.getMethod();
        if (Method.POST != method) {
            return handleServerResponse(CResponse.errorWithStrJson("请求方法不支持"));
        }

        // 解析请求体
        try {
            session.parseBody(new HashMap<>());
        } catch (Exception ex) {
            ex.printStackTrace();

            return this.handleServerResponse(CResponse.errorWithStrJson("请求体解析异常"));
        }

        Map<String, List<String>> parameters = session.getParameters();
        if (parameters == null || parameters.isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("无法解析到任何请求参数"));
        }

        PrinterConfig printerConfig = MapUtil.convert(parameters, PrinterConfig.class);
        if (printerConfig == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请求参数解析异常"));
        }

        Integer numOfCopies = printerConfig.getNumOfCopies();
        if (numOfCopies == null || numOfCopies <= 0) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印数量配置不正确"));
        }

        String mode = printerConfig.getMode();
        if (mode == null || mode.isEmpty() || mode.trim().isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印色彩配置不正确"));
        }

        String duplex = printerConfig.getDuplex();
        if (duplex == null || duplex.isEmpty() || duplex.trim().isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请正确配置单面打印或双面打印"));
        }

        String collate = printerConfig.getCollate();
        if (collate == null || collate.isEmpty() || collate.trim().isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请正确配置逐份打印参数"));
        }

        String paper = printerConfig.getPaper();
        if (paper == null || paper.isEmpty() || paper.trim().isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请正确配置纸张尺寸"));
        }

        String feeder = printerConfig.getFeeder();
        if (feeder == null || feeder.isEmpty() || feeder.trim().isEmpty()) {
            return this.handleServerResponse(CResponse.errorWithStrJson("请配置纸张来源"));
        }

        PrintSetting printSetting = new PrintSetting();
        printSetting.gammaIndex = 0x00;
        printSetting.halftoneIndex = 0x00;
        printSetting.numOfCopies = numOfCopies;
        printSetting.collate = collate;
        printSetting.feeder = feeder;
        printSetting.paper = paper;
        printSetting.duplex = duplex;
        printSetting.mode = mode;
        printSetting.paperType = "";

        boolean result = printDevice.setPrintSetting(printSetting);
        if (result) {
            // 将配置保存到配置文件
            String printSettingString = JSON.toJSONString(printerConfig);

            ConfigurationUtil configurationUtil = app.getConfigurationUtil();
            configurationUtil.putString(Common.PRINT_CONFIG, printSettingString);

            return handleServerResponse(CResponse.successWithStrJson("打印机配置成功"));
        } else {
            return handleServerResponse(CResponse.successWithStrJson("打印机配置失败，请稍后重试"));
        }
    }

    /**
     * 清空打印队列
     */
    private Response clearPrintQueue(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        boolean result = printDevice.clearPrintEnqueueFile();
        if (result) {
            return handleServerResponse(CResponse.successWithStrJson("打印队列清除成功"));
        } else {
            return handleServerResponse(CResponse.successWithStrJson("打印队列清除失败，请稍后重试"));
        }
    }

    /**
     * 添加打印文件
     */
    private Response addPrintFile(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        Method method = session.getMethod();
        if (Method.POST != method) {
            return handleServerResponse(CResponse.errorWithStrJson("请求方法不支持"));
        }

        // 解析上传的图片文件
        Map<String, String> filesPath = new HashMap<>();
        try {
            session.parseBody(filesPath);
        } catch (Exception ex) {
            ex.printStackTrace();

            return this.handleServerResponse(CResponse.errorWithStrJson("请求体解析异常"));
        }

        if (filesPath.isEmpty()) {
            return handleServerResponse(CResponse.errorWithStrJson("没有解析到图片文件"));
        }

        String uploadedFilePath = filesPath.get("file");
        if (uploadedFilePath == null) {
            return handleServerResponse(CResponse.errorWithStrJson("图片文件解析失败"));
        }

        // 移动文件到本地指定目录
        FileUtil fileUtil = new FileUtil(mContext);
        try {
            fileUtil.moveFile(uploadedFilePath);
        } catch (Exception ex) {
            ex.printStackTrace();

            return handleServerResponse(CResponse.errorWithStrJson("图片文件保存失败"));
        }

        return handleServerResponse(CResponse.successWithStrJson("文件上传成功"));
    }

    /**
     * 开始打印
     */
    private Response startPrint(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        Method method = session.getMethod();
        if (Method.POST != method) {
            return handleServerResponse(CResponse.errorWithStrJson("请求方法不支持"));
        }

        // 获取打印配置
        ConfigurationUtil configurationUtil = app.getConfigurationUtil();
        String printConfigStr = configurationUtil.getString(Common.PRINT_CONFIG, null);
        if (printConfigStr == null) {
            return handleServerResponse(CResponse.errorWithStrJson("请先配置打印参数"));
        }

        PrintSetting printSetting = JSON.parseObject(printConfigStr, PrintSetting.class);
        if (printSetting == null) {
            return handleServerResponse(CResponse.errorWithStrJson("打印参数失效，请先配置打印参数"));
        }

        FileUtil fileUtil = new FileUtil(mContext);

        // 提取出所有待打印文件
        List<File> fileList = fileUtil.getAllFile();
        if (fileList == null || fileList.isEmpty()) {
            return handleServerResponse(CResponse.errorWithStrJson("请先上传要打印的图片文件"));
        }

        for (File file : fileList) {
            String path = file.getPath();
            printDevice.enqueuePrintFile(path);
        }

        printDevice.enqueuePrintFile(null);
        DeviceManager.PrintResult printResult = printDevice.startPrint();

        // 打印成功，清空打印文件夹
        if (printResult == DeviceManager.PrintResult.SUCCESS) {
            fileUtil.clearFolder();

            return handleServerResponse(CResponse.successWithStrJson("打印成功"));
        }

        return handleServerResponse(CResponse.errorWithStrJson("打印失败，请稍后重试"));
    }

    /**
     * 取消打印
     */
    private Response cancelPrint(IHTTPSession session) {
        if (!Boolean.TRUE.equals(app.isConfigPrinter())) {
            return handleServerResponse(CResponse.successWithStrJson("打印机未正确配置"));
        }

        DeviceManager printDevice = app.getPrintDevice();
        if (printDevice == null) {
            return this.handleServerResponse(CResponse.errorWithStrJson("打印机未正确初始化，请重启此应用并再次尝试"));
        }

        boolean result = printDevice.cancelPrint();
        if (result) {
            // 清空打印文件夹
            FileUtil fileUtil = new FileUtil(mContext);
            fileUtil.clearFolder();

            return handleServerResponse(CResponse.successWithStrJson("取消成功"));
        } else {
            return this.handleServerResponse(CResponse.errorWithStrJson("取消失败"));
        }
    }

    @Override
    public Response serve(IHTTPSession session) {
        if (Method.OPTIONS.equals(session.getMethod())) {
            return createCorsResponse();
        }

        String uri = session.getUri();
        Function<IHTTPSession, Response> handler = routes.get(uri);

        if (handler != null) {
            return handler.apply(session);
        }

        return handleServerResponse(CResponse.errorWithStrJson("无法识别的请求路径"));
    }

    /**
     * 处理服务器响应
     * 服务器响应统一为JSON格式
     */
    private Response handleServerResponse(JSONObject response) {
        return newFixedLengthResponse(Response.Status.OK, JSON_MIME, response.toJSONString());
    }

    /**
     * 处理服务器跨域问题
     */
    private Response createCorsResponse() {
        Response response = newFixedLengthResponse(Response.Status.NO_CONTENT, "text/plain", "");

        response.addHeader("Access-Control-Allow-Origin", "*"); // 允许所有源
        response.addHeader("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE"); // 允许的方法
        response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); // 允许的请求头

        return response;
    }
}
